/********************************/
/*	Author:	gong_libin	*/
/*	Date:	2010_01_15	*/
/********************************/

#include "Base64.h"

static UCHAR sg_uszBase64Table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

UCHAR *Base64Encode(UCHAR *puszSrc,int iInLength,int *piOutLength)
{
	int iOutLength = 0;
        int iLineLength = 0;
	UCHAR *puszIn = NULL;
	UCHAR *puszEnd = NULL;
	UCHAR *puszDst = NULL;
	UCHAR *puszPos = NULL;

	iOutLength = iInLength * 4 / 3 + 4;
        iOutLength += iOutLength / 72;
	iOutLength += 1;
	puszDst = (UCHAR *)malloc(sizeof(UCHAR) * iOutLength);
	if( puszDst == NULL ) {
		return NULL;
	}

	puszIn = puszSrc;
	puszEnd = puszSrc + iInLength;
	puszPos = puszDst;

	while( puszEnd - puszIn >= 3 ) {
		*puszPos ++ = sg_uszBase64Table[puszIn[0] >> 2];
		*puszPos ++ = sg_uszBase64Table[((puszIn[0] & 0x03) << 4) | (puszIn[1] >> 4)];
		*puszPos ++ = sg_uszBase64Table[((puszIn[1] & 0x0f) << 2) | (puszIn[2] >> 6)];
		*puszPos ++ = sg_uszBase64Table[puszIn[2] & 0x3f];
		puszIn += 3;
		iLineLength += 4;
		if( iLineLength >= 72 ) {
			*puszPos ++ = '\n';
			iLineLength = 0;
		}
	}

	if( puszEnd - puszIn ) {
		*puszPos ++ = sg_uszBase64Table[puszIn[0] >> 2];
		if( puszEnd - puszIn == 1 ) {
			*puszPos ++ = sg_uszBase64Table[(puszIn[0] & 0x03) << 4];
			*puszPos ++ = '=';
		}
		else {
			*puszPos ++ = sg_uszBase64Table[((puszIn[0] & 0x03) << 4) | (puszIn[1] >> 4)];
			*puszPos ++ = sg_uszBase64Table[(puszIn[1] & 0x0f) << 2];
		}
		*puszPos ++ = '=';
		iLineLength += 4;
	}

	*puszPos = '\0';
	if( piOutLength ) {
		*piOutLength = puszPos - puszDst;
	}

	return puszDst;
}

UCHAR *Base64Decode(UCHAR *puszSrc,int iInLength,int *piOutLength)
{
	int i = 0;
	int iCount = 0;
	int iOutLength = 0;
	UCHAR ucTmp;
	UCHAR *puszDst = NULL;
	UCHAR *puszPos = NULL;
	UCHAR uszIn[4] = { 0 };
	UCHAR uszBlock[4] = { 0 };
	UCHAR uszTable[256] = { 0 };

	memset(uszTable,0x80,sizeof(uszTable));
	for( i = 0 ; i < sizeof(sg_uszBase64Table) - 1; i ++ ) {
		uszTable[sg_uszBase64Table[i]] = (UCHAR)i;
	}
	uszTable['='] = 0;

	for( i = 0; i < iInLength ; i ++ ) {
		if( uszTable[puszSrc[i]] != 0x80 ) {
			iCount ++;
		}
	}

	if( iCount % 4 ) {
		return NULL;
	}

	iOutLength = iCount / 4 * 3;
	puszPos = puszDst = (UCHAR *)malloc(sizeof(UCHAR) * iOutLength);
	if( puszDst == NULL ) {
		return NULL;
	}

	iCount = 0;
	for( i = 0 ; i < iInLength ; i ++ ) {
		ucTmp = uszTable[puszSrc[i]];
		if( ucTmp == 0x80 )
			continue;

		uszIn[iCount] = puszSrc[i];
		uszBlock[iCount] = ucTmp;
		iCount ++;
		if( iCount == 4 ) {
			*puszPos ++ = (uszBlock[0] << 2) | (uszBlock[1] >> 4);
			*puszPos ++ = (uszBlock[1] << 4) | (uszBlock[2] >> 2);
			*puszPos ++ = (uszBlock[2] << 6) | uszBlock[3];
			iCount = 0;
		}
	}

	if( puszPos > puszDst ) {
		if( uszIn[2] == '=' )
			puszPos -= 2;
		else if( uszIn[3] == '=' )
			puszPos --;
	}

	*piOutLength = puszPos - puszDst;

	return puszDst;
}
